//
//  build.cpp
//  
//
//  Created by Nikita Luzyanin on 30.11.13.
//
//

#include <iostream>
#include <fstream>
#include <iomanip>
#include <stdlib.h>
#include <stdio.h>
using namespace std;

int checkFile(char *pathToFile)
{
    FILE *f;
    f = fopen(pathToFile, "rb");
    if (f == NULL) {
        cout << pathToFile << " does not exist\n";
        return 1;
    }
    fclose(f);
    return 0;
}

int getLengthOfFile(char *pathToFile) {
    FILE *f;
    f = fopen(pathToFile, "rb");
    fseek(f,0,SEEK_END);
    int lengthOfFile = ftell(f);
    fclose(f);
    return lengthOfFile;
}

int getInfo(int &numberOfDisks, int &numberOfBlocks, int &sizeOfBlock, char raidType[3], int &positionToWriteFrom, char diskName[100])
{
    fstream disk;
    disk.open(diskName);
    disk >> numberOfDisks;
    disk >> numberOfBlocks;
    disk >> sizeOfBlock;
    disk >> raidType;
    positionToWriteFrom = disk.tellg();
    disk.close();
    return 0;
}

int clearBlockOnDisk(char diskName[100], int positionToClearFrom, int sizeOfBlock)
{
    fstream disk;
    disk.open(diskName);
    disk.seekp(positionToClearFrom);
    for (int i = 0; i < sizeOfBlock; i ++) {
        disk << "0";
    }
    disk.close();
    return 0;
}

int checkDisks(int &numberOfDisks, int &numberOfBlocks, int &sizeOfBlock, char raidType[3], int &lengthOfMetaBlock, char missingDisksNames[5][100], int &numberOfDisksMissing)
{
    bool infoGot = false;
    numberOfDisks = 5;
    numberOfDisksMissing = 0;
    
    char diskName[10];
    char diskNumber[10];
    for (int numberOfDisk = 0; numberOfDisk < numberOfDisks; numberOfDisk++) {
        sprintf(diskNumber, "%i", numberOfDisk);
        strcpy(diskName, "disk");
        strcat(diskName, diskNumber);
        if (checkFile(diskName) != 0) {
            strcpy(missingDisksNames[numberOfDisksMissing], diskName);
            numberOfDisksMissing  ++;
        } else {
            if (infoGot == false) {
                if (getInfo(numberOfDisks, numberOfBlocks, sizeOfBlock, raidType, lengthOfMetaBlock, diskName) != 0) {
                    cout << "Mistake\n";
                    return 1;
                }
            } else {
                if (sizeOfBlock != getLengthOfFile(diskName)) {
                    cout << "Sizes of disk vary\n";
                    return 1;
                }
            }
        }
    }
    return 0;
}

int checkExistanceOfBlock(int numberOfDisks, int sizeOfBlock, int numberOfBlockToCheck, int lengthOfMetaBlock)
{
    fstream CB;
    char diskName[10];
    char diskNumber[10];
    int positionToCheckFrom = lengthOfMetaBlock + numberOfBlockToCheck * sizeOfBlock + 1;
    
    for (int numberOfDisk = 0; numberOfDisk < numberOfDisks; numberOfDisk++) {
        sprintf(diskNumber, "%i", numberOfDisk);
        strcpy(diskName, "disk");
        strcat(diskName, diskNumber);
        CB.open(diskName);
        CB.seekp(positionToCheckFrom);
        if (CB.get() == '0') {
            cout << "This block is empty\nUnable to read\n";
            return 1;
        }
        CB.close();
    }
    
    return 0;
}

int makeParity(char parity[2048], char dataForEachDisk[5][2048], int lengthForDisk, int numberOfDisks, int diskForParity)
{
    for (int bytesXORed = 0; bytesXORed < lengthForDisk; bytesXORed ++) {
        bool firstRun = true;
        for (int numberOfDisk = 0; numberOfDisk < numberOfDisks; numberOfDisk ++) {
            if (numberOfDisk != diskForParity) {
                if (firstRun == true) {
                    parity[bytesXORed] = dataForEachDisk[numberOfDisk][bytesXORed];
                    firstRun = false;
                } else {
                    parity[bytesXORed] = parity[bytesXORed] xor dataForEachDisk[numberOfDisk][bytesXORed];
                }
            }
        }
    }

    return 0;
}

int checkParity(char parity[2048], char dataForEachDisk[5][2048], int lengthForDisk, int numberOfDisks, int diskForParity)
{
    char parityTest;
    
    for (int bytesXORed = 0; bytesXORed < lengthForDisk; bytesXORed ++) {
        bool firstRun = true;
        for (int numberOfDisk = 0; numberOfDisk < numberOfDisks; numberOfDisk ++) {
            if (numberOfDisk != diskForParity) {
                if (firstRun == true) {
                    parityTest = dataForEachDisk[numberOfDisk][bytesXORed];
                    firstRun = false;
                } else {
                    parityTest = parityTest xor dataForEachDisk[numberOfDisk][bytesXORed];
                }
            }
        }
        if (parityTest != parity[bytesXORed]) {
            cout << "Mistake in comparing with parity\n";
            return 1;
        }
    }
    
    return 0;
}

int copyBlock(char srcDiskName[5], char dstDiskName[5], int positionToCopyFrom)
{
    fstream SD, DD;
    char ch;
    int lengthForBlock;
    SD.open(srcDiskName);
    DD.open(dstDiskName);
    SD.seekp(positionToCopyFrom);
    DD.seekp(positionToCopyFrom);
    SD >> lengthForBlock;
    ch = SD.get();
    DD << lengthForBlock;
    DD << "\n";
    
    for (int i = 0; i < lengthForBlock; i ++) {
        ch = SD.get();
        DD << ch;
    }
    
    SD.close();
    DD.close();
    return 0;
}

int checkEqualityOfBlocks(char srcDiskName[5], char dstDiskName[5], int positionToCheckFrom)
{
    fstream SD, DD;
    char sch, dch;
    int lengthForSrcBlock, lengthForDstBlock;
    SD.open(srcDiskName);
    DD.open(dstDiskName);
    SD.seekp(positionToCheckFrom);
    DD.seekp(positionToCheckFrom);
    SD >> lengthForSrcBlock;
    sch = SD.get();
    DD >> lengthForDstBlock;
    dch = DD.get();
    
    if (lengthForDstBlock != lengthForSrcBlock) {
        return 1;
    }
    
    for (int i = 0; i < lengthForSrcBlock; i ++) {
        sch = SD.get();
        dch = DD.get();
        if (sch != dch) {
            return 1;
        }
    }
    
    SD.close();
    DD.close();
    return 0;
    return 0;
}